<?php

/**
 * 自动加载
 */
spl_autoload_register(static function (string $class): void {
    $namespacePrefix = 'core\\';
    $baseDirectory   = realpath(__DIR__) . DIRECTORY_SEPARATOR;
    $prefixLength    = strlen($namespacePrefix);

    // 检查前缀
    if (strncmp($namespacePrefix, $class, $prefixLength) !== 0) {
        return;
    }

    // 提取相对类名部分
    $relativeClass = substr($class, $prefixLength);
    // 验证相对类名
    if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\\\\]*$/', $relativeClass)) {
        return;
    }

    // 构建文件系统路径，将命名空间分隔符替换为目录分隔符
    $filePath = str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass);
    // 使用 realpath() 进行最终规范化和存在性检查
    $realPath = realpath($baseDirectory . $filePath . '.php');

    // 确保解析后的实际路径仍在允许的基目录内
    if ($realPath !== false && str_starts_with($realPath, $baseDirectory)) {
        if (is_file($realPath)) {
            require_once $realPath;
        }
    }
});

/**
 * 渲染一个视图模板。
 *
 * @param string $view 模板名称 (例如 'user.profile')。
 * @param array<string, mixed> $data 传递给视图的数据。
 * @param string|null $viewPath (可选) 自定义模板根目录。
 * @param string|null $cachePath (可选) 自定义缓存目录。
 * @return string 渲染后的 HTML 内容。
 */
function view(string $view, array $data = [], ?string $viewPath = null, ?string $cachePath = null): string
{
    /** @var View|null $instance 静态变量，用于存储 View 类的单例 */
    static $instance = null;

    if ($instance === null) {
        $defaultViewPath  = $viewPath ??  APP_PATH . '/view';
        $defaultCachePath = $cachePath ?? RUNTIME_PATH . '/view';

        // 创建 View 实例
        $instance = new core\View($defaultViewPath, $defaultCachePath);
    }

    return $instance->render($view, $data);
}

/**
 * 检查当前请求是否为 POST 请求
 *
 * @return bool 如果是 POST 请求返回 true，否则返回 false
 */
function is_post(): bool
{
    // 确保 $_SERVER['REQUEST_METHOD'] 被设置并且为 POST
    return ($_SERVER['REQUEST_METHOD'] ?? null) === 'POST';
}

/**
 * 终止请求并返回指定的 HTTP 状态码和错误信息。
 *
 * @param int $code HTTP 状态码（如 404、403、500）。
 * @param string $message 错误信息 (可选)
 * @return void
 * @throws InvalidArgumentException 如果状态码无效。
 */
function abort(int $code, string $message = ''): void
{
    // 定义支持的状态码及其默认错误信息
    $defaultMessages = [
        403 => '无访问权限',
        404 => '页面不存在',
        500 => '服务器内部错误',
    ];

    // 检查状态码是否有效
    if (!array_key_exists($code, $defaultMessages)) {
        throw new InvalidArgumentException("无效的HTTP状态码: {$code}");
    }

    // 使用自定义消息或默认消息
    $message = $message ?: $defaultMessages[$code];

    // 设置 HTTP 响应码
    if (!headers_sent()) {
        http_response_code($code);
    }

    // 渲染错误页面
    $templateFile = CORE_PATH . '/templates/abort.php';
    require $templateFile;

    exit(1);
}

/**
 * 转义字符串以安全地输出到 HTML 上下文
 *
 * @param string $str 待转义的原始字符串
 * @return string 转义后的 HTML 安全字符串
 */
function e(string $str): string
{
    return htmlspecialchars($str, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

/**
 * 生成一次性 CSRF Token，并存入 Session。
 * Token 在验证后会立即销毁（一次性使用）。
 *
 * @param int $length 随机字节长度（最终 token 字符串长度 = $length * 2）
 * @return string 返回生成的十六进制 Token 字符串
 * @throws Exception 当 random_bytes() 生成失败时抛出
 */
function generate_token(int $length = 32): string
{
    $token = bin2hex(random_bytes($length));
    $_SESSION['csrf_token'] = $token;

    return $token;
}

/**
 * 调试输出
 *
 * @param mixed ...$value 要输出的值
 * @return void
 */
function dump(...$value): void
{
    // 开启输出缓冲区
    ob_start();
    var_dump(...$value);
    $output = ob_get_clean();

    // 格式化输出内容
    $output = preg_replace('/]=>\n(\s+)/m', '] => ', $output); // 压缩换行
    $output = htmlspecialchars($output, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); // 转义 HTML 特殊字符
    $output = sprintf('<pre>%s</pre>', $output);

    // 输出结果
    echo $output;
}

/**
 * 调试输出并终止
 *
 * @param mixed ...$value 要输出的变量。
 * @return void
 */
function dd(...$value): void
{
    dump(...$value);
    exit(1);
}
